package com.uroboros.spring.guides.rest.employee;

import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;

import java.util.List;
import org.springframework.hateoas.CollectionModel;
import org.springframework.hateoas.EntityModel;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class EmployeeController {

  private final EmployeeRepository repository;
  private final EmployeeModelAssembler assembler;

  public EmployeeController(EmployeeRepository repository, EmployeeModelAssembler assembler) {
    this.repository = repository;
    this.assembler = assembler;
  }

  @GetMapping("/employees")
  List<Employee> all() {
    return repository.findAll();
  }

  @PostMapping("/employees")
  Employee newEmployee(@RequestBody Employee newEmployee) {
    return repository.save(newEmployee);
  }


  @GetMapping("/employees/{id}")
  Employee one(@PathVariable Long id) {
    return repository.findById(id).orElseThrow(() -> new EmployeeNotFoundException(id));
  }

  @PutMapping("/employees/{id}")
  Employee replaceEmployee(@RequestBody Employee newEmployee, @PathVariable Long id) {
    return repository.findById(id)
        .map(employee -> {
          employee.setName(newEmployee.getName());
          employee.setRole(newEmployee.getRole());
          return repository.save(employee);
        })
        .orElseGet(() -> repository.save(newEmployee));
  }

  @DeleteMapping("/employees/{id}")
  void deleteEmployee(@PathVariable Long id) {
    repository.deleteById(id);
  }

  /**
   * 超链接驱动的获取单个员工信息
   */
  @GetMapping("/hateoas/employees/{id}")
  EntityModel<Employee> oneByHypermediaDriven(@PathVariable Long id) {
    Employee employee = repository.findById(id).orElseThrow(() -> new EmployeeNotFoundException(id));

    return EntityModel.of(employee,
        linkTo(methodOn(EmployeeController.class).one(id)).withSelfRel(),
        linkTo(methodOn(EmployeeController.class).all()).withRel("employees"));
  }

  /**
   * 超链接驱动的获取所有员工信息
   */
  @GetMapping("/hateoas/employees")
  CollectionModel<EntityModel<Employee>> allByHypermediaDriven() {
    List<EntityModel<Employee>> employees = repository.findAll().stream()
        .map(employee -> EntityModel.of(employee,
            linkTo(methodOn(EmployeeController.class).oneByHypermediaDriven(employee.getId())).withSelfRel(),
            linkTo(methodOn(EmployeeController.class).allByHypermediaDriven()).withRel("employees"))).toList();
    return CollectionModel.of(employees, linkTo(methodOn(EmployeeController.class).allByHypermediaDriven()).withSelfRel());
  }

  /**
   * 超链接驱动的获取单个员工信息（简化）
   */
  @GetMapping("/hateoas/simplify/employees/{id}")
  EntityModel<Employee> oneByHypermediaDrivenSimplify(@PathVariable Long id) {
    return assembler.toModel(repository.findById(id).orElseThrow(() -> new EmployeeNotFoundException(id)));
  }

  /**
   * 超链接驱动的获取所有员工信息（简化）
   */
  @GetMapping("/hateoas/simplify/employees")
  CollectionModel<EntityModel<Employee>> allByHypermediaDrivenSimplify() {
    return assembler.toCollectionModel(repository.findAll());
  }

}